package com.cloudkinto.service.cost.impl;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.common.BeanConvert;
import com.cloudkinto.common.common.DateUtils;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.CostDao;
import com.cloudkinto.dao.CostItemDao;
import com.cloudkinto.entity.CostDo;
import com.cloudkinto.entity.CostItemDo;
import com.cloudkinto.entity.WarehouseDo;
import com.cloudkinto.enumType.operate.OperateTypeEnum;
import com.cloudkinto.service.company.CompanyService;
import com.cloudkinto.service.cost.CostService;
import com.cloudkinto.service.cost.importVo.CostExportVo;
import com.cloudkinto.service.cost.vo.CostWorkAddReq;
import com.cloudkinto.service.cost.vo.CostWorkItemAddReq;
import com.cloudkinto.service.file.FileMoreService;
import com.cloudkinto.service.operate.OperateRecordService;
import com.cloudkinto.service.warehouse.WarehouseService;
import com.cloudkinto.utils.BeanCopierUtils;
import com.cloudkinto.vo.cost.*;
import com.cloudkinto.vo.cost.out.CostItemForOrderPageDto;
import com.cloudkinto.vo.file.FileMsg;
import com.cloudkinto.vo.file.FileRes;
import com.cloudkinto.vo.operate.OperateRes;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * service实现类
 * </p>
 *
 * @author hua
 * @since 2024-11-29
 */
@Service
@Transactional(rollbackFor = Exception.class)//事务回滚
public class CostServiceImpl extends ServiceImpl<CostDao, CostDo> implements CostService {
    @Autowired
    private CostDao dao;
    @Autowired
    private WarehouseService warehouseService;
    @Autowired
    private CompanyService companyService;
    @Autowired
    private FileMoreService fileMoreService;
    @Autowired
    private CostItemDao costItemDao;
    @Autowired
    private OperateRecordService operateRecordService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${gbc.customer}")
    private String customer;

    @Override
    public void costWordAdd(CostWorkAddReq req, Long userId, Long tenantId) {
        CostInfoEntity entity = new CostInfoEntity();
        entity.setStatus(req.getStatus());
        entity.setCostStatus(2);
        entity.setCostTime(new Date());
        entity.setOrderNo(req.getOrderNo());
        entity.setOrderId(req.getOrderId());
        entity.setModuleType(StaticDict.Cost_ModuleType.Fourth.getValue());
        entity.setOrderType(req.getOrderType());
        entity.setWhId(req.getWhId());
        entity.setCompanyId(req.getCompanyId());
        entity.setTenantId(tenantId);
        entity.setOmsOrderCreateTime(req.getOmsOrderCreateTime());
        entity.setCurrencyCode(req.getCurrencyCode());
        entity.setCostTotal(req.getCostTotal());
        List<CostItemEt> items = new ArrayList<>();
        for (CostWorkItemAddReq item : req.getItems()) {
            items.add(new CostItemEt(item.getItemName(), item.getCallDetail(), item.getTotalFee(), "", item.getCalVolume()));
        }
        entity.setItems(items);
        this.insertEntity(entity, userId);
    }


    @Override
    public void insertEntity(CostInfoEntity entity, Long userId) {
        //除了退件都可能有 前置计费
        //不管他 先删除 之前得
        if (StaticDict.Cost_ModuleType.First.getValue().equals(entity.getModuleType()) || StaticDict.Cost_ModuleType.Second.getValue().equals(entity.getModuleType())
                || StaticDict.Cost_ModuleType.Fourth.getValue().equals(entity.getModuleType())) {
            dao.updateDelete(entity.getOrderId(), entity.getOrderType(), entity.getModuleType(), userId, entity.getCompanyId());
        } else if (StaticDict.Cost_ModuleType.Third.getValue().equals(entity.getModuleType())) {
            //仓储费
        }
        CostDo costDo = BeanConvert.INSTANCE.costDo(entity);
        //生成单据号
        costDo.setCostNo(this.getCostNo(entity.getStatus(), entity.getTenantId()));
        dao.insert(costDo);
        if (entity.getItems() != null) {
            for (CostItemEt item : entity.getItems()) {
                CostItemDo costItemDo = new CostItemDo();
                BeanUtils.copyProperties(item, costItemDo);
                costItemDo.setCostId(costDo.getId());
                costItemDao.insert(costItemDo);
            }
        }
    }

    public String getCostNo(Integer status, Long tenantId) {
        Date date = new Date();
        long count = 0;

            //改成放到redis 计数
            String key = "cost_count_" + status + "_" + tenantId + "_" + customer + "_" + DateUtils.dateFormatToString(new Date(), "yyyyMMdd");
            count = redisTemplate.opsForValue().increment(key, 1);
            redisTemplate.expire(key, 60 * 60 * 25, TimeUnit.SECONDS);
        StringBuilder builder = new StringBuilder((count) + "");
        while (builder.length() < 4) {
            builder.insert(0, "0");
        }
        builder.insert(0, DateUtils.dateFormatToString(new Date(), "yyyyMMdd")).insert(0, 0 == status ? "TR#" : "TA#");
        return builder.toString();
    }
    @Override
    public Object add(CostAddReq req, Long userId, Long tenantId) {
        WarehouseDo warehouseDo = warehouseService.getById(req.getWhId());

        CostDo entityDo = new CostDo();
        BeanUtils.copyProperties(req, entityDo);
        entityDo.setCostNo(this.getCostNo(0, tenantId));
        entityDo.setTenantId(tenantId);
        entityDo.setCurrencyCode(warehouseDo.getCurrencyCode());
        entityDo.setOrderType(StaticDict.Cost_Input_OrderType.Fifth.getValue());
        entityDo.setCostStatus(2);
        entityDo.setCostBy(userId);
        entityDo.setManualAdjust(true);
        entityDo.setCostTime(new Date());
        dao.insert(entityDo);
        CostItemDo costItemDo = new CostItemDo();
        costItemDo.setCostId(entityDo.getId());
        costItemDo.setItemName("---");
        costItemDo.setBillType(1001);//手动补录
        costItemDo.setAuto(1);
        costItemDo.setTips(req.getTips());
//        costItemDo.setBillType(StaticDict.Cost_Input_OrderType.Fifth.getValue());
        costItemDo.setTotalFee(entityDo.getCostTotal().doubleValue());
        costItemDao.insert(costItemDo);

        List<Long> fileIds = req.getFiles().stream().map(FileMsg::getFileId).filter(Objects::nonNull).collect(Collectors.toList());
        fileMoreService.insertFileMoreList(fileIds, entityDo.getId(), StaticDict.File_Type.Cost.getValue(), userId, null);
        operateRecordService.operateAddRecordAndValue("新建费用成功。\n总费用为：\"" + entityDo.getCostTotal().doubleValue() + "\"", "", entityDo.getId(), userId, null, OperateTypeEnum.Cost);
        return entityDo.getId();
    }

    @Override
    public int delete(Long id, Long userId) {
        CostDo entityDo = dao.selectById(id);
        return dao.deleteById(id);
    }

    @Override
    public Object detail(Long id, Long tenantId) {
        CostDetailVo entityDo = dao.getCostById(id, tenantId);
        CostDetailRes res = new CostDetailRes();
        BeanUtils.copyProperties(entityDo, res);
        List<CostItemDo> costItemDos = costItemDao.selectList(new LambdaQueryWrapper<CostItemDo>().eq(CostItemDo::getCostId, id));
        List<CostItemRes> costItemRes = BeanCopierUtils.listCopyMapper(costItemDos, CostItemDo.class, CostItemRes.class);
        costItemRes.forEach(i -> i.setCurrencyCode(res.getCurrencyCode()));
        res.setItems(costItemRes);
        List<FileRes> fileList = fileMoreService.getFileList(id, StaticDict.File_Type.Cost.getValue());
        List<OperateRes> operateList = operateRecordService.getOperateList(id, OperateTypeEnum.Cost);
        res.setOperateResList(operateList);
        res.setFiles(fileList);
        return res;
    }


    @Override
    public SingleResult pageInit(String source, Long userId, Long companyId, Long tenantId) {
        Map<String, Object> result = new HashMap<>();
        if ("omp".equals(source)) {
            result.put("warehouseList", warehouseService.getList(tenantId));
            result.put("companyList", companyService.getCompanyList(userId, tenantId, source));
        } else {
            result.put("warehouseList", warehouseService.getWarehouseCompany(companyId, tenantId));
        }
        result.put("moduleTypeList", StaticDict.getCost_ModuleType());
        result.put("inputOrderTypeList", StaticDict.getCost_Input_OrderType());
        result.put("outOrderTypeList", StaticDict.getCost_Out_OrderType());
        //业务费用详情 单据类型
        result.put("billType", StaticDict.getOperateBill_Type());
        return SingleResult.success(result);
    }

    @Override
    public List<CostPageVo> getList(Map<String, Object> map) {
        return dao.list(map);
    }

    @Override
    public PageResult getListPage(Map<String, Object> map) {
        IPage<CostPageVo> page = pageInit(map);
        page = dao.list(page, map);
//        for (CostPageDto record : page.getRecords()) {
//            if (StaticDict.Cost_ModuleType.First.getValue().equals(record.getModuleType())) {
//
//            }
//        }
        PageResult result = new PageResult(page.getRecords(), (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    private IPage<CostPageVo> pageInit(Map<String, Object> map) {
        IPage<CostPageVo> page = new Page();
        page.setSize(map.get("pageSize") != null ? Long.parseLong(map.get("pageSize").toString()) : 10);
        page.setCurrent(map.get("current") != null ? Long.parseLong(map.get("current").toString()) : 1);
        return page;
    }


    @Override
    public void manualAdjust(CostManualAdjustWmsReq req, Long userId) {
        //根据订单号 获取costId
        CostDo costDo = dao.selectOne(new LambdaQueryWrapper<CostDo>()
                .eq(CostDo::getOrderId, req.getOrderId()).eq(CostDo::getOrderType, req.getOrderType()).last(" order by id desc limit 1"));
        if (costDo == null) {
            throw new BizException(SysConstant.No_Data);
        }
        this.manualAdjust(new CostManualAdjustOmpReq(costDo.getId(), req.getItems()), userId, costDo.getTenantId());
    }


    @Override
    public void manualAdjust(CostManualAdjustOmpReq req, Long userId, Long tenantId) {
        CostDo et = dao.selectById(req.getId());
        if (et == null || et.getCostStatus() != 2) return;
        if (!Objects.equals(tenantId, et.getTenantId())) return;
        //只有出库 入库 才能添加

        List<CostItemDo> itemDoList = costItemDao.selectList(new LambdaQueryWrapper<CostItemDo>().eq(CostItemDo::getCostId, req.getId()));
        List<Long> reqIds = req.getItems().stream().map(CostManualAdjustItemReq::getId).filter(Objects::nonNull).collect(Collectors.toList());
        List<Long> deleteIds = itemDoList.stream().map(CostItemDo::getId).filter(i -> !reqIds.contains(i)).collect(Collectors.toList());
        Map<Long, CostItemDo> costItemMap = itemDoList.stream().collect(Collectors.toMap(CostItemDo::getId, i -> i));
        if (!deleteIds.isEmpty()) {
            costItemDao.updateDelete(deleteIds, userId);
        }
        double totalFee = 0d;
        for (CostManualAdjustItemReq item : req.getItems()) {
            if (item.getId() == null) {
                //新增
                if (!et.getModuleType().equals(StaticDict.Cost_ModuleType.First.getValue()) && !StaticDict.Cost_ModuleType.Second.getValue().equals(et.getModuleType())) {
                    continue;
                }
                CostItemDo costItemDo = new CostItemDo();
                BeanUtils.copyProperties(item, costItemDo);
                costItemDo.setCostId(et.getId());
                costItemDao.insert(costItemDo);
                totalFee += costItemDo.getTotalFee();
            } else {
                CostItemDo costItemDo = costItemMap.get(item.getId());
                if (costItemDo == null) continue;
                boolean changFlag = false;
                if (!Objects.equals(costItemDo.getTotalFee(), item.getTotalFee())) {
                    changFlag = true;
                }
                BeanUtils.copyProperties(item, costItemDo);
                if (changFlag) {
                    costItemDo.setCallDetail("--");
                }
                costItemDao.updateById(costItemDo);
                totalFee += costItemDo.getTotalFee();
            }
        }
        et.setCostTotal(BigDecimal.valueOf(totalFee));
        et.setManualAdjust(true);
        dao.updateById(et);

        operateRecordService.operateAddRecordAndValue("手动调整费用", "", et.getId(), userId, null, OperateTypeEnum.Cost);

    }


    @Override
    public List<CostItemForOrderPageDto> getCostItem(List<Long> orderIds, String orderType, Long tenantId) {
        if (orderIds == null || orderIds.isEmpty()) return new ArrayList<>();
        return dao.getItemList(orderIds, orderType, tenantId);
    }


    @Override
    public void exportCost(Map<String, Object> map, Long tenantId, HttpServletResponse response) {
        map.put("status", 0);//实扣
        List<CostPageVo> costList = dao.list(map);
        List<CostExportVo> exportVos = BeanConvert.INSTANCE.costExportVo(costList);


        // 创建参数对象（用来设定excel得sheet得内容等信息）
        ExportParams costRentParams = new ExportParams();
        // 设置sheet得名称
        costRentParams.setSheetName("业务流水");
        // 创建sheet1使用得map
        Map<String, Object> costRentMap = new HashMap<>();
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        costRentMap.put("title", costRentParams);
        // 模版导出对应得实体类型
        costRentMap.put("entity", CostExportVo.class);
        // sheet中要填充得数据
        costRentMap.put("data", exportVos);
        List<Map<String, Object>> sheetsList = new ArrayList<>();
        sheetsList.add(costRentMap);
        try {
            ExcelUtils.exportExcel(sheetsList, "业务费用" + DateUtils.dateFormatToString(new Date(), "HHmmss"), response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }


}
